package edu.uwm.cs552;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;

import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;


/**
 * A component that displays a hexboard
 */
public class HexBoardPanel extends JPanel implements Scrollable, Observer {
	public static final int MIN_SCALE = 10;
	private static final int DEFAULT_SCALE = 20;

	/**
	 * Keep Eclipse happy
	 */
	private static final long serialVersionUID = 1L;
	
	private HexBoard board;
	private double scale;
	private boolean drawMesh;
	private boolean showHidden;
	private Set<HexCoordinate> selected = new HashSet<HexCoordinate>();
	private final double BOUND = 0.5;
	private Player player;
	private RailGame game;
	
	/**
	 * Create a hex panel that views this board.
	 * Initially the scale is 20.
	 * By default, the mesh is off, hidden georaphy is not shown,
	 *  and no coordinate is selected.
	 * @param b initial board, must not be null
	 */
	public HexBoardPanel(HexBoard b) {
		board = b;
		scale = DEFAULT_SCALE;
		board.addObserver(this);
	}
	
	/**
	 * Get the status of the "draw mesh" field.
	 * If this is set, we draw a hexagonal mesh on top of the board.
	 * @return whether we are drawing the mesh
	 */
	public boolean getDrawMesh() {
		return drawMesh;
	}
	
	/**
	 * Set the status of the "draw mesh" field.
	 * If this is set, we draw a hexagonal mesh on top of the board.
	 * @param dm whether to draw the mesh
	 */
	public void setDrawMesh(boolean dm) {
		drawMesh = dm;
		repaint();
	}
	
	/**
	 * Get the status of the "show hidden" field.
	 * If this is set, barriers are explicit and suburbs shown
	 * in a contrasting color.
	 * @return whether we are showing hidden geography or not
	 */
	public boolean getShowHidden() {
		return showHidden;
	}
	
	/**
	 * Set the status of the "showHidden" field.
	 * If this is set, barriers are explicit and suburbs shown in
	 * a contrasting color.
	 * @param sh whether to show hidden geography.
	 */
	public void setShowHidden(boolean sh) {
		showHidden = sh;
		repaint();
	}
	
	/**
	 * Current scale board is being viewed at.
	 * @return current width of hexagons
	 */
	public double getScale() {
		return scale;
	}
	
	/**
	 * Set the scale to a new value, must be at least 10.
	 * @param s scale to use, must be >= 10.
	 */
	public void setScale(double s) {
		if (s <= 0) throw new IllegalArgumentException("scale must be positive: " + s);
		if (s < MIN_SCALE) s = MIN_SCALE;
		scale = s;
		resize();
	}
	
	public double getBound() {
		return BOUND;
	}

	
	/**
	 * Set the size of this panel if the background or magnification changes.
	 */
	private void resize() {
		this.setSize(board.getWidth(scale), board.getHeight(scale));
		this.setPreferredSize(this.getSize());
		repaint();
	}
	
	
	/// Selection (currently unused)
	
	/**
	 * Clear all selected hex coordinates.
	 */
	public void clearSelection() {
		List<HexCoordinate> copy = new ArrayList<HexCoordinate>(selected);
		for (HexCoordinate h : copy) {
			toggleSelection(h);
		}
	}
	
	/**
	 * The hex coordinate is selected if it wasn't before,
	 * and is unselected if it was selected before.
	 * @param h
	 */
	public void toggleSelection(HexCoordinate h) {
		if (!selected.add(h)) selected.remove(h);
		final Graphics g = getGraphics();
		g.setXORMode(Color.MAGENTA);
		paintSelection(g,h);
	}
	
	/**
	 * The hex coordinate is selected if i wasn't already.
	 * @param h
	 */
	public void addSelection(HexCoordinate h) {
		if (!selected.add(h)) return;
		final Graphics g = getGraphics();
		g.setXORMode(Color.MAGENTA);
		paintSelection(g,h);
	}
	
	/**
	 * Get a readonly view of the current selection.
	 * @return view of selected hex coordinates
	 */
	public Iterable<HexCoordinate> getSelection() {
		return Collections.unmodifiableCollection(selected);
	}
	
	private void paintSelection(Graphics g, HexCoordinate h) {
		g.fillPolygon(h.toPolygon(scale));
	}
	
	
	@Override 
	public void paint(Graphics g) {
		super.paint(g);
		board.draw(g, scale, showHidden, drawMesh);
		if (game != null) {
			game.draw((Graphics2D)g, scale, player);
		}
		/*g.setXORMode(Color.MAGENTA);
		for (HexCoordinate h : selected) {
			paintSelection(g,h);
		}*/
	}

	
	public void update(Observable arg0, Object arg1) {
//		if (arg1 == arg0) {
//			resize();
//		} else {
//			
//			repaint();
//		}
		if (arg0 instanceof Player) {
			Player p = (Player)arg0;
			if (game == null) {
				player = p;
				game = p.getGame();
			}
			else {
				player = player != p ? null : p;
			}
		}
			
		repaint();
	}

	
	/// The following methods are used to making scroll bars work.
	

	public Dimension getPreferredScrollableViewportSize() {
		return getPreferredSize();
	}

	public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
		if (orientation == SwingConstants.HORIZONTAL) {
			return visibleRect.width - getScrollableUnitIncrement(visibleRect,orientation,direction);
		} else {
			return visibleRect.height - getScrollableUnitIncrement(visibleRect,orientation,direction);
		}
	}

	public boolean getScrollableTracksViewportHeight() {
		return false;
	}

	public boolean getScrollableTracksViewportWidth() {
		return false;
	}

	private static final double SQRT32 = Math.sqrt(3)/2;
	
	public int getScrollableUnitIncrement(Rectangle arg0, int orientation, int direction) {
		// if some clicks the control, we go one hexagon width/height at a time.
		if (orientation == SwingConstants.HORIZONTAL) {
			return (int)scale;
		} else {
			return (int)(scale*SQRT32);
		}
	}
	
	
}
